package unionpay

import (
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"
	"fmt"
	"hash"
	"io/ioutil"
	"net/url"
	"sort"
	"strings"
	"time"

	"jgt-wallet/src/gopay/util"

	"golang.org/x/crypto/pkcs12"
)

// B2bConfig 配置文件信息
type B2bConfig struct {
	SellerID string `xml:"seller_id"` //合作者ID
	SignCer  struct {
		Path string `xml:"path"`
		Pwd  string `xml:"pwd"`
		Type string `xml:"type"`
	} `xml:"sign_cer"`
	MiddleCer string `xml:"middle_cer"`
	RootCer   string `xml:"root_cer"`
	EncCer    string `xml:"enc_cer"`
	SecureKey string `xml:"secure_key"`
}

// B2bClient 支付客户端参数
type B2bClient struct {
	SellerID string //合作者ID
	SignCer  struct {
		PrivateKey *rsa.PrivateKey
		PublicKey  *rsa.PublicKey
		Certifate  *x509.Certificate
		CaCers     []*x509.Certificate
	}
	MiddleCer struct {
		PublicKey *rsa.PublicKey
		Certifate *x509.Certificate
	}
	RootCer struct {
		PublicKey *rsa.PublicKey
		Certifate *x509.Certificate
	}
	EncCer struct {
		Certifate *x509.Certificate
	}
	SecureKey string
}

// NewB2bClient 创建新银联B2B支付
func NewB2bClient(conf *B2bConfig) (*B2bClient, error) {
	var (
		client                              = new(B2bClient)
		block                               *pem.Block
		signCer, middleCer, rootCer, encCer []byte
		privateKey                          interface{}
		err                                 error
	)

	client.SellerID = conf.SellerID
	client.SecureKey = conf.SecureKey

	//读取证书文件
	if signCer, err = ioutil.ReadFile(conf.SignCer.Path); err != nil {
		return nil, err
	}

	if middleCer, err = ioutil.ReadFile(conf.MiddleCer); err != nil {
		return nil, err
	}

	if rootCer, err = ioutil.ReadFile(conf.RootCer); err != nil {
		return nil, err
	}

	if encCer, err = ioutil.ReadFile(conf.EncCer); err != nil {
		return nil, err
	}

	privateKey, client.SignCer.Certifate, client.SignCer.CaCers, err = pkcs12.Decode(signCer, conf.SignCer.Pwd)
	if err != nil {
		return nil, err
	}
	client.SignCer.PrivateKey = privateKey.(*rsa.PrivateKey)
	client.SignCer.PublicKey = client.SignCer.Certifate.PublicKey.(*rsa.PublicKey)

	block, _ = pem.Decode(middleCer)
	client.MiddleCer.Certifate, err = x509.ParseCertificate(block.Bytes)
	if err != nil {
		return nil, err
	}
	client.MiddleCer.PublicKey = client.MiddleCer.Certifate.PublicKey.(*rsa.PublicKey)

	block, _ = pem.Decode(rootCer)
	client.RootCer.Certifate, err = x509.ParseCertificate(block.Bytes)
	if err != nil {
		return nil, err
	}
	client.RootCer.PublicKey = client.RootCer.Certifate.PublicKey.(*rsa.PublicKey)

	block, _ = pem.Decode(encCer)
	client.EncCer.Certifate, err = x509.ParseCertificate(block.Bytes)
	if err != nil {
		return nil, err
	}
	return client, err
}

//Pay 银联B2B支付
func (a *B2bClient) Pay(charge *Charge) (string, error) {
	frontTransUrl := `https://gateway.test.95516.com/gateway/api/frontTransReq.do`

	data := map[string]string{}

	data["version"] = "5.1.0"
	data["encoding"] = "UTF-8"
	data["signMethod"] = "01"
	data["txnType"] = "01"
	data["txnSubType"] = "01"
	data["bizType"] = "000202"
	data["channelType"] = "07"

	// 商户接入参数
	data["merId"] = a.SellerID
	data["accessType"] = "0"
	data["orderId"] = charge.TradeNum
	data["txnTime"] = time.Now().Format("20060102150406")
	data["currencyCode"] = "156"
	data["txnAmt"] = util.MoneyFeeToFenString(charge.MoneyFee)

	data["frontUrl"] = charge.CallbackURL
	data["backUrl"] = charge.CallbackURL
	data["payTimeout"] = time.Now().Add(1 * time.Hour).Format("20060102150406")

	data["signature"] = a.GenSign(data)

	return makeHtml(frontTransUrl, data["encoding"], data), nil
}

// QueryOrder 订单查询
func (a *B2bClient) QueryOrder(outTradeNo string) (*QueryResult, error) {
	var (
		urlStr, queryStr string
		query            = map[string]string{}
		value            url.Values
		err              error
		bs               []byte
		data             map[string]string
		result           = new(QueryResult)
	)
	urlStr = `https://gateway.95516.com/gateway/api/queryTrans.do`

	query["version"] = "5.1.0"
	query["encoding"] = "UTF-8"
	query["signMethod"] = "11"
	query["txnType"] = "00"
	query["txnSubType"] = "00"
	query["bizType"] = "000802"

	query["merId"] = a.SellerID
	query["accessType"] = "0"

	query["orderId"] = outTradeNo
	query["txnTime"] = time.Now().Format("20060102130405")

	for k, v := range query {
		if queryStr != "" {
			queryStr = fmt.Sprintf("%s&%s=%s", queryStr, k, v)
		} else {
			queryStr = fmt.Sprintf("%s=%s", k, v)
		}
	}

	value, err = url.ParseQuery(queryStr)
	bs, err = util.HTTPSC.PostData(urlStr, "application/x-www-form-urlencoded", value.Encode())
	if err != nil {
		return nil, err
	}
	data = parseDataToMap(bs)
	err = a.CheckSign(data)
	if err != nil {
		return nil, err
	}

	err = util.MapStringToStruct(data, result)

	return result, err
}

// GenSign 产生签名
func (a *B2bClient) GenSign(m map[string]string) string {

	switch m["signMethod"] {
	case "01":
		var data []string
		for k, v := range m {
			if v != "" && k != "signature" {
				data = append(data, fmt.Sprintf(`%s=%s`, k, v))
			}
		}
		sort.Strings(data)
		signData := strings.Join(data, "&")

		s := sha256.New()
		_, err := s.Write([]byte(signData))
		if err != nil {
			panic(err)
		}
		hashByte := s.Sum(nil)
		signByte, err := a.SignCer.PrivateKey.Sign(rand.Reader, hashByte, crypto.SHA256)
		if err != nil {
			panic(err)
		}
		return base64.StdEncoding.EncodeToString(signByte)
	case "11":
		var data []string
		for k, v := range m {
			if v != "" && k != "signature" {
				data = append(data, fmt.Sprintf(`%s=%s`, k, v))
			}
		}
		sort.Strings(data)
		signData := strings.Join(data, "&")

		signData = fmt.Sprintf("%s&%s", signData, util.Sha256X16Srt(a.SecureKey))

		return util.Sha256X16Srt(signData)
	case "12":
		return "加密方式未实现"
	default:
		return "加密方式不存在"
	}

}

// CheckSign 验签
func (a *B2bClient) CheckSign(m map[string]string) error {
	var (
		sign, signData     string
		signByte, hashByte []byte
		data               []string
		s                  hash.Hash
		err                error
	)
	sign = m["signature"]
	for k, v := range m {
		if v != "" && k != "signature" {
			data = append(data, fmt.Sprintf(`%s=%s`, k, v))
		}
	}
	sort.Strings(data)
	signData = strings.Join(data, "&")
	signByte, err = base64.StdEncoding.DecodeString(sign)
	if err != nil {
		return err
	}
	s = crypto.SHA256.New()
	_, err = s.Write([]byte(signData))
	if err != nil {
		return err
	}
	hashByte = s.Sum(nil)
	err = rsa.VerifyPKCS1v15(a.RootCer.PublicKey, crypto.SHA256, hashByte, signByte)
	return err
}

// makeHtml 生成HTML
func makeHtml(reqUrl, encoding string, data map[string]string) string {
	var (
		html, head, form, body, script string
	)
	head = fmt.Sprintf("<head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\"/></head>", encoding)
	form = fmt.Sprintf("<form id = \"pay_form\" action=\"%s\" method=\"post\">", reqUrl)
	for k, v := range data {
		form = fmt.Sprintf("%s<input type = \"hidden\" name = \"%s\" id=\"%s\" value=\"%s\"/>", form, k, k, v)
	}
	form = fmt.Sprintf("%s</form>", form)
	body = fmt.Sprintf("<body>%s</body>", form)
	script = fmt.Sprintf("<script type=\"text/javascript\">")
	script = fmt.Sprintf("%s document.all.pay_form.submit();</script>", script)
	html = fmt.Sprintf("<html> %s %s %s </html>", head, body, script)

	return html
}
